package com.ruleengine.instruction;

import java.util.ArrayList;
import java.util.List;

public class InstructionSet {
    String instructionSetId;
    List<Instruction> instructions = new ArrayList<Instruction>();
    ExceptionTable expTable = new ExceptionTable();

    public InstructionSet() {

    }

    public InstructionSet(String instructionSetId) {
        super();
        this.instructionSetId = instructionSetId;
    }

    public String getInstructionSetId() {
        return instructionSetId;
    }

    public void setInstructionSetId(String instructionSetId) {
        this.instructionSetId = instructionSetId;
    }

    public void addInstructions(List<Instruction> instructions) {
        this.instructions.addAll(instructions);
    }

    public void addInstruction(Instruction instruction) {
        //设置指令在指令组中的偏移量
        instruction.setInstructionOffset(this.instructions.size());
        this.instructions.add(instruction);
    }

    public List<Instruction> getInstructions() {
        return instructions;
    }

    public Instruction getInstruction(int index) {
        return instructions.get(index);
    }
    
    /**
     * 返回指令在指令数组中的偏移量.
     * 索引位置从0开始，如果不存在该指令，则返回-1
     * @param in 查询的指令
     * @return 如果指令存在则返回索引位置，否则返回-1
     */
    public int indexOf(Instruction in) {
        return instructions.indexOf(in);
    }

    public void setInstructions(List<Instruction> instructions) {
        this.instructions = instructions;
    }

    /**
     * 获取指令的总数
     * @return
     */
    public int size() {
        return this.instructions.size();
    }

    /**
     * 获取某一条指令对应的异常处理指令的偏移量
     * @param instIndex 当前指令的偏移量
     * @param cause 当前异常
     * @return
     */
    public int getExceptionHandler(int instIndex, Throwable cause) {
        return expTable.getExceptionHandler(instIndex, cause.getClass());
    }

    /**
     * 给工作流任务定义异常处理指令
     * @param from 异常处理包含的指令（try块）的起始偏移量
     * @param to   异常处理包含的指令（try块）的结束偏移量
     * @param target 异常处理块（catch块）的起始指令偏移量
     * @param causes 针对的异常
     */
    public void addExceptionHandler(int from, int to, int target,
            List<String> causes) {
        List<Class<Throwable>> l = new ArrayList<Class<Throwable>>();
        for (String c : causes) {
            try {
                // 初始化执行器
                Class<Throwable> cl = (Class<Throwable>) Class.forName(c);
                l.add(cl);
            } catch (Exception e) {
                throw new RuntimeException("loading Class error :" + c);
            }
        }

        ExceptionEntry entry = new ExceptionEntry(from, to, target);
        entry.from = from;
        entry.to = to;
        entry.target = target;
        entry.causes = l;
        expTable.addExceptionHandler(entry);

    }

    private class ExceptionTable {
        //按entry的ExceptionEntry.to升序排列
        List<ExceptionEntry> entrys = new ArrayList<ExceptionEntry>();

        public int getExceptionHandler(int instIndex, Class cause) {
            for (ExceptionEntry entry : entrys) {
                if (entry.from <= instIndex && entry.to > instIndex) {
                    for (Class<Throwable> c : entry.causes) {
                        if (c.isAssignableFrom(cause)) {
                            return entry.target;
                        }
                    }
                }
            }
            return -1;
        }

        public void addExceptionHandler(ExceptionEntry entry) {
            int i = 0;
            for(; i < entrys.size(); i++){
                ExceptionEntry e = entrys.get(i);
                if(e.compareTo(entry)>0){
                    break;
                }
            }
            entrys.add(i, entry);
        }

        public List<ExceptionEntry> getEntrys() {
            return entrys;
        }

        public void setEntrys(List<ExceptionEntry> entrys) {
            this.entrys = entrys;
        }

        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return entrys.toString();
        }

        /* private int searchLastMinIndex(int instIndex, int low, int high){
             if(high > low ){
                 int mid = (low+high)/2;
                 if( entrys.get(mid).from == instIndex){
                     //from
                 }else if( entrys.get(mid).from > instIndex){
                     //
                     return searchLastMinIndex(instIndex, mid+1, high);
                 }else if( entrys.get(mid).from < instIndex){
                     return searchLastMinIndex(instIndex, low, mid-1);
                 }
             }
             if(high == low){
                 //没有找到from和instIndex相等的
                 //那么就找第一个小于instIndex的
                 if(entrys.get(high).from == instIndex)
             }
             //已经折半到最后
             return -1;
         }*/

    }

    private class ExceptionEntry implements Comparable<ExceptionEntry> {
        int from;
        int to;
        int target;
        List<Class<Throwable>> causes;

        public ExceptionEntry(int from, int to, int target) {
            super();
            this.from = from;
            this.to = to;
            this.target = target;
        }

        public int compareTo(ExceptionEntry o) {
            // TODO Auto-generated method stub
            return to > o.to ? 1 : to == o.to ? 0 : -1;
        }

        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return "[" + from + ", " + to + ", " + target + ", " + causes + "]";
        }

    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "{" + instructionSetId + ", \n" + " instructions --->" + instructions
                + ", \n expTable-->" + expTable + "}";
    }

    public static void main(String[] args) {
        InstructionSet a = new InstructionSet();
        ExceptionTable t = a.new ExceptionTable();
        t.addExceptionHandler(a.new ExceptionEntry(1, 10, 10));
        t.addExceptionHandler(a.new ExceptionEntry(3, 8, 8));
        t.addExceptionHandler(a.new ExceptionEntry(5, 7, 7));
        System.out.println(t.getEntrys());
    }

}
